package com.zwh.minio;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import io.minio.http.Method;
import io.minio.messages.Item;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import io.minio.*;
import io.minio.messages.Bucket;

/**
 * @author zwh
 * @date 2024-3-25 17:17
 */
@ConditionalOnProperty(prefix = "minio", name = "enabled")
@Component
public class MinioUtil {
    private final MinioClient minioClient;

    public MinioUtil(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    /**
     * 创建一个桶
     */
    public void createBucket(String bucketName) {
        try {
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!found) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            throw new MinioRuntimeException(e);
        }
    }
    /**
     * 判断bucket是否存在。
     *
     * @param bucketName 桶名称。
     * @return 存在返回true，否则false。
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new MinioRuntimeException(e);
        }
    }

    /**
     * 上传一个文件,控制文件分段上传大小为10MB
     *
     * @param bucketName 桶名称。
     * @param objectName 对象名称。
     * @param stream     上传的文件流。
     * @throws Exception
     */
    public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        try{
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName)
                // objectSize: 对象实际大小，-1表示对象大小未知，上传时检测
                // partSize: (10485760 = 1024 * 1024 * 10)分段上传，每段大小，如果这个值超过objectSize,则直接partSize=objectSize
                //           传-1，表示自动控制分段上传大小，默认为：1024 * 1024 * 5
                .stream(stream, stream.available(), 10485760).build());
        } catch (Exception e) {
            throw new MinioRuntimeException(e);
        }
//        minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName)
//                .stream(stream, -1, 10485760).build());
    }
    /**
     * 下载一个文件
     * @param bucketName
     * @param objectName
     * @return 返回文件流，使用完需要关闭
     */
    public InputStream getObject(String bucketName, String objectName) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            throw new MinioRuntimeException(e);
        }
    }
    /**
     * 获取存储的文件对象。
     *
     * @param bucketName     桶名称。
     * @param objectName 对象名称。
     * @param dir 下载文件到指定目录, 如果最后未存在 /符号，会自动添加
     * @return 读取后存储到文件的文件对象。
     */
    public File getObjectToDir(String bucketName, String objectName, String dir) {
        if (StrUtil.isNotBlank(dir)) {

            dir = dir.endsWith("/") ? dir : dir + "/";
        }
        File file = new File(dir + objectName);;
        try (InputStream in = this.getObject(bucketName, objectName);
            FileOutputStream outputStream = new FileOutputStream(file)) {
            File fileDir = new File(dir);
            if (!fileDir.exists() || fileDir.isFile()) {
                fileDir.mkdirs();
            }
            IoUtil.copy(in, outputStream);
        } catch (Exception e) {
            throw new MinioRuntimeException(e);
        }
        return file;
    }
    /**
     * @description:获取文件预览接口
     * @date: 2022/8/18 9:44
     * @param: fileName: 文件名
     * @Param: bucketName: 桶名
     * @Param: previewExpiry: 预览到期时间（小时）
     * @return: java.lang.String 返回文件url
     **/
    public String getPreviewUrl(String bucketName, String fileName, Integer previewExpiry) {
        if (StrUtil.isNotBlank(fileName)) {
            try {
                minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
                if (null != previewExpiry){
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(fileName)
                        .expiry(previewExpiry, TimeUnit.HOURS)
                        .build());
                } else {
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(fileName)
                        .build());
                }
            } catch (Exception e) {
                throw new MinioRuntimeException(e);
            }
        }
        return null;
    }
    /**
     * 删除一个桶
     */
    public void deleteBucket(String bucketName) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }
    /**
     * 删除一个对象
     */
    public void deleteObject(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }
    /**
     * 列出所有的桶
     */
    public List<String> listBuckets() throws Exception {
        List<Bucket> list = minioClient.listBuckets();
        List<String> names = new ArrayList<>();
        list.forEach(b -> {
            names.add(b.name());
        });
        return names;
    }
    /**
     * 列出一个桶中的所有文件和目录
     */
    public List<Fileinfo> listFiles(String bucketName) throws Exception {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).recursive(true).build());

        List<Fileinfo> infos = new ArrayList<>();
        results.forEach(r->{
            Fileinfo info = new Fileinfo();
            try {
                Item item = r.get();
                info.setFilename(item.objectName());
                info.setDirectory(item.isDir());
                infos.add(info);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return infos;
    }

}
